package com.xjrsoft.module.app.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.AuthorizeType;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.enums.MenuType;
import com.xjrsoft.common.enums.YesOrNoEnum;
import com.xjrsoft.common.model.tree.ITreeNode;
import com.xjrsoft.common.utils.CommonUtil;
import com.xjrsoft.common.utils.TreeUtil;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.app.entity.*;
import com.xjrsoft.module.app.mapper.*;
import com.xjrsoft.module.app.service.IAppAuthorizeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.module.desktop.entity.DesktopSchema;
import com.xjrsoft.module.desktop.entity.UserDesktopRelation;
import com.xjrsoft.module.desktop.mapper.DesktopSchemaMapper;
import com.xjrsoft.module.desktop.mapper.UserDesktopRelationMapper;
import com.xjrsoft.module.desktop.vo.DesktopSchemaInfoVo;
import com.xjrsoft.module.magicapi.entity.InterfaceAuth;
import com.xjrsoft.module.magicapi.mapper.InterfaceAuthMapper;
import com.xjrsoft.module.organization.dto.SetRoleAuthDto;
import com.xjrsoft.module.organization.entity.Department;
import com.xjrsoft.module.organization.entity.Post;
import com.xjrsoft.module.organization.entity.Role;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.mapper.RoleMapper;
import com.xjrsoft.module.organization.mapper.UserRoleRelationMapper;
import com.xjrsoft.module.organization.vo.RoleAuthVo;
import com.xjrsoft.module.organization.vo.UserRoleVo;
import com.xjrsoft.module.system.entity.*;
import com.xjrsoft.module.system.vo.MenuAuthVo;
import com.xjrsoft.module.system.vo.MenuFormTreeVo;
import com.xjrsoft.module.system.vo.MenuTreeVo;
import com.xjrsoft.module.system.vo.PermissionVo;
import lombok.AllArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hnyyzy
 * @since 2023-12-25
 */
@Service
@AllArgsConstructor
public class AppAuthorizeServiceImpl extends ServiceImpl<AppAuthorizeMapper, AppAuthorize> implements IAppAuthorizeService {

    private final UserRoleRelationMapper userRoleRelationMapper;

    private final AppMenuMapper appMenuMapper;

    private final AppMenuButtonMapper appMenuButtonMapper;

    private final AppMenuColumnMapper appMenuColumnMapper;

    private final AppMenuFormMapper appMenuFormMapper;

    private final AppAuthorizeMapper appAuthorizeMapper;

    private final RoleMapper roleMapper;

    @Override
    public MenuAuthVo getAppPermissions(Long menuId) {

        long userId = StpUtil.getLoginIdAsLong();
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getUserId, userId));

        List<Long> relationIds = relations.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());

        List<Role> roleList = roleMapper.selectList(Wrappers.lambdaQuery(Role.class)
                .select(Role.class, x -> VoToColumnUtil.fieldsToColumns(UserRoleVo.class).contains(x.getColumn()))
                .in(relationIds.size() > 0, Role::getId, relationIds));

        List<Long> roleIdList = roleList.stream().map(Role::getId).collect(Collectors.toList());

        //如果包含超级管理员权限  默认返回当前菜单所有权限
        List<AppMenuButton> buttonList = appMenuButtonMapper.selectList(Wrappers.<AppMenuButton>lambdaQuery().eq(AppMenuButton::getMenuId,menuId));
        List<AppMenuColumn> columnList = appMenuColumnMapper.selectList(Wrappers.<AppMenuColumn>lambdaQuery().eq(AppMenuColumn::getMenuId,menuId));
        List<AppMenuForm> formList = appMenuFormMapper.selectList(Wrappers.<AppMenuForm>lambdaQuery().eq(AppMenuForm::getMenuId,menuId));
        if (!roleIdList.contains(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            List<AppAuthorize> authorizeList = appAuthorizeMapper.selectList(Wrappers.lambdaQuery(AppAuthorize.class)
                    .in(roleIdList.size() > 0,AppAuthorize::getRoleId, roleIdList)
            );

            List<Long> buttonIds = authorizeList.stream().filter(x -> x.getAppAuthorizeType() == AuthorizeType.BUTTON.getCode()).map(AppAuthorize::getObjectId).collect(Collectors.toList());
            if (buttonIds.size() > 0) {
                buttonList = buttonList.stream().filter(x->buttonIds.contains(x.getId())).collect(Collectors.toList());
            }else {
                buttonList = new ArrayList<>(0);
            }

            List<Long> columnIds = authorizeList.stream().filter(x -> x.getAppAuthorizeType() == AuthorizeType.COLUMN.getCode()).map(AppAuthorize::getObjectId).collect(Collectors.toList());
            if (columnIds.size() > 0) {
                columnList = columnList.stream().filter(x->columnIds.contains(x.getId())).collect(Collectors.toList());
            }else {
                columnList = new ArrayList<>(0);
            }

            List<Long> formIds = authorizeList.stream().filter(x -> x.getAppAuthorizeType() == AuthorizeType.FORM.getCode()).map(AppAuthorize::getObjectId).collect(Collectors.toList());
            if (formIds.size() > 0) {
                LambdaQueryWrapper<AppMenuForm> queryWrapper = Wrappers.lambdaQuery(AppMenuForm.class);
                queryWrapper.in(AppMenuForm::getId, formIds);
                formList = formList.stream().filter(x->formIds.contains(x.getId())).collect(Collectors.toList());
            }
            else {
                formList = new ArrayList<>(0);
            }
        }

        List<MenuFormTreeVo> formTreeVoList = TreeUtil.build(BeanUtil.copyToList(formList, MenuFormTreeVo.class));

        MenuAuthVo vo = new MenuAuthVo();
        vo.setMenuId(menuId);
        vo.setButtonAuthCode(buttonList.stream().map(AppMenuButton::getCode).collect(Collectors.toSet()));
        vo.setColumnAuthCode(columnList.stream().map(AppMenuColumn::getCode).collect(Collectors.toSet()));
        // 处理表单字段，可能存在子表的情况
        Set<Object> formAuthCodeList = new LinkedHashSet<>(formTreeVoList.size());
        for (MenuFormTreeVo treeVo : formTreeVoList) {
            List<MenuFormTreeVo> children = treeVo.getChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                Set<String> subCodeList = new LinkedHashSet<>(children.size());
                for (MenuFormTreeVo child : children) {
                    subCodeList.add(child.getCode());
                }
                Map<String, Set<String>> subTableCodeMap = new HashMap<>(1);
                subTableCodeMap.put(treeVo.getCode(), subCodeList);
                formAuthCodeList.add(subTableCodeMap);
            } else {
                formAuthCodeList.add(treeVo.getCode());
            }
        }
        vo.setFormAuthCode(formAuthCodeList);

        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean setRoleAppAuth(SetRoleAuthDto dto) {
        //添加菜单权限
        Long id = dto.getId();
        List<AppAuthorize> savedAuthList = new ArrayList<>();
        //菜单
        buildAppAuthorize(id, dto.getMenuIds(), AuthorizeType.MENU.getCode(), savedAuthList);
        //按钮
        buildAppAuthorize(id, dto.getButtonIds(), AuthorizeType.BUTTON.getCode(), savedAuthList);
        //列表字段
        buildAppAuthorize(id, dto.getColumnIds(), AuthorizeType.COLUMN.getCode(), savedAuthList);
        //表单字段
        buildAppAuthorize(id, dto.getFormIds(), AuthorizeType.FORM.getCode(), savedAuthList);

        //删除当前角色所有权限
        this.remove(Wrappers.<AppAuthorize>query().lambda().eq(AppAuthorize::getRoleId, id).in(AppAuthorize::getAppAuthorizeType, 0, 1, 2, 3));
        //先保存角色权限
        this.saveBatch(savedAuthList);
        //再更新角色权限的缓存
        logoutByRoleId(id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RoleAuthVo getRoleAppAuth(Long id) {
        //如果是超级管理员 默认返回所有权限
        List<AppMenu> menus = appMenuMapper.selectList(Wrappers.lambdaQuery(AppMenu.class)
                .eq(AppMenu::getEnabledMark, EnabledMark.ENABLED.getCode()));
        if (Objects.equals(id, GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            List<AppMenuButton> buttons = appMenuButtonMapper.selectList(Wrappers.lambdaQuery(AppMenuButton.class).select(AppMenuButton::getId));

            RoleAuthVo adminVo = new RoleAuthVo();
            adminVo.setMenuIds(menus.stream().map(AppMenu::getId).collect(Collectors.toList()));
            adminVo.setButtonIds(buttons.stream().map(AppMenuButton::getId).collect(Collectors.toList()));
            return adminVo;
        }

        //获取角色所有权限
        List<AppAuthorize> authorizeList = appAuthorizeMapper.selectList(Wrappers.lambdaQuery(AppAuthorize.class).eq(AppAuthorize::getRoleId, id));
        //将角色权限分为 菜单、按钮、列表字段和表单字段权限
        List<Long> menuIdList = new ArrayList<>();
        List<Long> btnIdList = new ArrayList<>();
        List<Long> colIdList = new ArrayList<>();
        List<Long> formIdList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(authorizeList)) {
            for (AppAuthorize authorize : authorizeList) {
                Integer authorizeType = authorize.getAppAuthorizeType();
                Long authorizeId = authorize.getObjectId();
                if (authorizeType == AuthorizeType.MENU.getCode()) {
                    menuIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.BUTTON.getCode()) {
                    btnIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.COLUMN.getCode()) {
                    colIdList.add(authorizeId);
                } else if (authorizeType == AuthorizeType.FORM.getCode()) {
                    formIdList.add(authorizeId);
                }
            }
        }
        RoleAuthVo roleAuthVo = new RoleAuthVo();
        if (CollectionUtils.isNotEmpty(menuIdList)) {
            List<Long> authMenuIdsList = menus.stream().filter(menu -> menuIdList.contains(menu.getId())).map(AppMenu::getId).collect(Collectors.toList());
            roleAuthVo.setMenuIds(authMenuIdsList);
        }
        if (CollectionUtils.isNotEmpty(btnIdList)) {
            // 过滤按钮id
            List<AppMenuButton> menuButtonList = appMenuButtonMapper.selectList(Wrappers.<AppMenuButton>query().lambda()
                    .select(AppMenuButton::getId).in(AppMenuButton::getId, btnIdList));
            roleAuthVo.setButtonIds(menuButtonList.stream().map(AppMenuButton::getId).collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(colIdList)) {
            // 过滤列表字段id
            List<AppMenuColumn> menuColumnList = appMenuColumnMapper.selectList(Wrappers.<AppMenuColumn>query().lambda()
                    .select(AppMenuColumn::getId).in(AppMenuColumn::getId, colIdList));
            roleAuthVo.setColumnIds(menuColumnList.stream().map(AppMenuColumn::getId).collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(formIdList)) {
            // 过滤掉按钮id
            List<AppMenuForm> menuList = appMenuFormMapper.selectList(Wrappers.<AppMenuForm>query().lambda()
                    .select(AppMenuForm::getParentId, AppMenuForm::getId));
            List<AppMenuForm> authMenuFormList = new ArrayList<>();
            Set<Long> parentIdSet = new LinkedHashSet<>();
            for (AppMenuForm menuForm : menuList) {
                if (formIdList.contains(menuForm.getId())) {
                    authMenuFormList.add(menuForm);
                }
                parentIdSet.add(menuForm.getParentId());
            }
            List<MenuFormTreeVo> menuFormTreeVos = BeanUtil.copyToList(authMenuFormList, MenuFormTreeVo.class);
            List<MenuFormTreeVo> menuFormTreeList = TreeUtil.build(menuFormTreeVos);
            buildLastChildIds(menuFormTreeList, roleAuthVo.getFormIds(), parentIdSet);
        }
        return roleAuthVo;
    }


    private <T extends ITreeNode<T, Long>> void buildLastChildIds(List<T> treeList, List<Long> idList, Set<Long> parentIdList) {
        if (CollectionUtils.isEmpty(treeList)) {
            return;
        }
        for (ITreeNode<T, Long> treeVo : treeList) {
            List<T> children = treeVo.getChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                buildLastChildIds(children, idList, parentIdList);
            } else if (!parentIdList.contains(treeVo.getId())){
                idList.add(treeVo.getId());
            }
        }
    }

    /**
     * 构建授权数据
     *
     * @param roleId          角色id
     * @param itemIdList      菜单id
     * @param itemType        菜单类型，0-菜单，1-按钮，2-列表字段，3-表单字段
     * @param authorizeList   构建的保存对象集合
     */
    private void buildAppAuthorize(Long roleId, List<Long> itemIdList, Integer itemType, List<AppAuthorize> authorizeList) {
        if (CollectionUtils.isNotEmpty(itemIdList)) {
            for (Long itemId : itemIdList) {
                AppAuthorize authorize = new AppAuthorize();
                authorize.setAppAuthorizeType(itemType);
                authorize.setObjectId(itemId);
                authorize.setRoleId(roleId);
                authorizeList.add(authorize);
            }
        }
    }

    /**
     * 根据roleId 登出所有用户  用于：角色权限变化，所有拥有此权限的用户 必须全部重新登录
     *
     * @param id
     */
    private void logoutByRoleId(Long id) {
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getRoleId, id).select(UserRoleRelation::getUserId));

        List<Long> currentRoleUser = relations.stream().map(UserRoleRelation::getUserId).collect(Collectors.toList());
        //过滤掉管理员用户,再进行退出登录的操作
        List<Long> allAdminUserIdList = CommonUtil.getAllAdminUserIdList();
        currentRoleUser = currentRoleUser.stream().filter(x-> CollectionUtil.isNotEmpty(allAdminUserIdList) && !allAdminUserIdList.contains(x)).collect(Collectors.toList());
        currentRoleUser.forEach(StpUtil::logout);
    }

}
